package com.yuanian.wf.utils;

import de.odysseus.el.util.SimpleContext;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.Execution;
import org.apache.commons.lang3.StringUtils;
import org.apache.el.ExpressionFactoryImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import java.util.List;
import java.util.Map;

@Component
public class ActivitiUtils {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private RepositoryService repositoryService;

    private static ActivitiUtils AU;

    @PostConstruct
    public void init() {
        AU = this;
    }

    /**
     * 获取下一个UserTask
     *
     * @param processDefinitionId
     * @param taskDefinitionKey
     * @param variables
     * @return
     */
    public static UserTask findNextUserTask(String processDefinitionId, String taskDefinitionKey,
                                            Map<String, Object> variables) {
        if (StringUtils.isAnyEmpty(processDefinitionId, taskDefinitionKey)) {
            return null;
        }

        ProcessDefinitionQuery processDefinitionQuery = AU.repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId);
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();

        BpmnModel bpmnModel = AU.repositoryService.getBpmnModel(processDefinition.getId());
        List<Process> processList = bpmnModel.getProcesses();
        if (processList != null && processList.size() > 0) {
            org.activiti.bpmn.model.Process process = processList.get(0);

            if (process != null) {

                /**
                 * 1.首先需要找到对应的UserTask
                 */
                List<UserTask> utasks = process.findFlowElementsOfType(UserTask.class);
                UserTask ut = null;
                for (UserTask u : utasks) {
                    String id = u.getId();
                    if (id.equals(taskDefinitionKey)) {
                        ut = u;
                        break;
                    }
                }

                if (null == ut) {
                    return null;
                }

                /**
                 * 2.获取outgoingFlows,并根据用户参数决定流程走向
                 */
                List<SequenceFlow> flows = ut.getOutgoingFlows();
                UserTask userTask = findFirstUserTask(flows, variables);
                return userTask;
            }
        }

        return null;
    }

    /**
     * 获取第一个UserTask
     *
     * @param processDefinitionId
     * @param variables
     * @return
     */
    public static UserTask findFirstUserTask(String processDefinitionId, Map<String, Object> variables) {
        if (StringUtils.isAnyEmpty(processDefinitionId)) {
            return null;
        }

        ProcessDefinitionQuery processDefinitionQuery = AU.repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId);
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();

        BpmnModel bpmnModel = AU.repositoryService.getBpmnModel(processDefinition.getId());
        List<org.activiti.bpmn.model.Process> processList = bpmnModel.getProcesses();
        if (processList != null && processList.size() > 0) {
            org.activiti.bpmn.model.Process process = processList.get(0);

            if (process != null) {
                /**
                 * 1.首先需要找到StartEvent
                 */
                List<StartEvent> events = process.findFlowElementsOfType(StartEvent.class);
                StartEvent startEvent = events.get(0);

                /**
                 * 2.获取outgoingFlows,并根据用户参数决定流程走向
                 */
                List<SequenceFlow> flows = startEvent.getOutgoingFlows();
                UserTask userTask = findFirstUserTask(flows, variables);
                return userTask;
            }
        }

        return null;
    }

    public static UserTask findFirstUserTask(List<SequenceFlow> flows, Map<String, Object> variables) {
        for (int i = 0; i < flows.size(); i++) {
            SequenceFlow flow = flows.get(i);
            String conditionExpression = flow.getConditionExpression();
            FlowElement flowElement2 = flow.getTargetFlowElement();
            if (StringUtils.isEmpty(conditionExpression) || isCondition(conditionExpression, variables)) {
                return execFlowElement(flowElement2, conditionExpression, variables);
            }
        }

        return null;
    }

    private static UserTask execFlowElement(FlowElement flowElement, String conditionExpression,
                                            Map<String, Object> variables) {
        if (StringUtils.isEmpty(conditionExpression) || isCondition(conditionExpression, variables)) {
            if (flowElement instanceof UserTask) {
                UserTask userTask = (UserTask) flowElement;
                return userTask;
            } else if (flowElement instanceof ExclusiveGateway) {
                ExclusiveGateway gateway = (ExclusiveGateway) flowElement;
                List<SequenceFlow> sfs = gateway.getOutgoingFlows();// 排它网关的下一个导向一定是流程线
                for (int i = 0; i < sfs.size(); i++) {
                    SequenceFlow sf = sfs.get(i);
                    FlowElement flowElement2 = sf.getTargetFlowElement();
                    if (sf instanceof SequenceFlow) {
                        String cond = sf.getConditionExpression();
                        if (StringUtils.isEmpty(cond) || isCondition(cond, variables)) {
                            return execFlowElement(flowElement2, cond, variables);
                        }
                    }
                }
            } else if (flowElement instanceof SequenceFlow) {
                SequenceFlow sf = (SequenceFlow) flowElement;
                FlowElement flowElement2 = sf.getTargetFlowElement();
                String cond = sf.getConditionExpression();
                if (StringUtils.isEmpty(cond) || isCondition(cond, variables)) {
                    return execFlowElement(flowElement2, cond, variables);
                }
            } else if (flowElement instanceof StartEvent) {

            } else if (flowElement instanceof EndEvent) {
            }
        }
        return null;
    }

    /**
     * 查询流程启动时设置排他网关判断条件信息
     *
     * @param String gatewayId 排他网关Id信息, 流程启动时设置网关路线判断条件key为网关Id信息
     * @param String processInstanceId 流程实例Id信息
     * @return
     */
    public static String getGatewayCondition(String gatewayId, String processInstanceId) {
        Execution execution = AU.runtimeService.createExecutionQuery().processInstanceId(processInstanceId)
                .singleResult();
        return AU.runtimeService.getVariable(execution.getId(), gatewayId).toString();
    }

    /**
     * 根据key和value判断el表达式是否通过信息
     *
     * @param String key el表达式key信息
     * @param String el el表达式信息
     * @param String value el表达式传入值信息
     * @return
     */
    public static boolean isCondition(String key, String el, String value) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        context.setVariable(key, factory.createValueExpression(value, String.class));
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        return (Boolean) e.getValue(context);
    }

    /**
     * 根据key和value判断el表达式是否通过信息
     *
     * @param el
     * @param variables
     * @return
     */
    public static boolean isCondition(String el, Map<String, Object> variables) {
        ExpressionFactory factory = new ExpressionFactoryImpl();
        SimpleContext context = new SimpleContext();
        variables.forEach((key, value) -> {
            context.setVariable(key, factory.createValueExpression(value, String.class));
        });
        ValueExpression e = factory.createValueExpression(context, el, boolean.class);
        return (Boolean) e.getValue(context);
    }

    public static boolean isCondition1(String el, Map<String, Object> variables) {
        SpelExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(el);
        EvaluationContext evaluationContext = new StandardEvaluationContext();
        for (Map.Entry<String, Object> entry : variables.entrySet()) {
            evaluationContext.setVariable(entry.getKey(), entry.getValue());
        }
        Object obj = expression.getValue(evaluationContext);
        if (obj instanceof Boolean) {
            return (Boolean)obj;
        } else {
            return false;
        }
    }

}
